﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace Apq.IO
{
	/// <summary>
	/// 本地文件缓存块(单块)
	/// </summary>
	public class FileSecCache
	{
		// 缓存最大值(2MB)
		private static int _MaxSize = 2048 << 10;

		/// <summary>
		/// 完整文件名
		/// </summary>
		[DefaultValue("")]
		public string FullFileName { get; set; }
		/// <summary>
		/// 关联的文件流
		/// </summary>
		public System.IO.Stream FS { get; set; }
		/// <summary>
		/// 本缓存块在流中的起点(相对流起点)
		/// </summary>
		[DefaultValue(0)]
		public long PosBegin { get; set; }

		/// <summary>
		/// 缓存区
		/// </summary>
		public List<byte> Cache { get; set; }
		/// <summary>
		/// 缓存区的锁,写入缓存区时需要使用
		/// </summary>
		protected object _lockCache = new object();

		/// <summary>
		/// 当前已刷到流的数据量(相对缓存起点)
		/// </summary>
		[DefaultValue(0)]
		protected int Flushed { get; set; }

		/// <summary>
		/// 默认构造函数,为成员赋予默认值
		/// </summary>
		private FileSecCache()
		{
			FullFileName = string.Empty;
		}

		/// <summary>
		/// 本地文件缓存块(单块)
		/// </summary>
		/// <param name="FullFileName">完整文件名</param>
		/// <param name="Stream">关联的流对象</param>
		/// <param name="PosBegin">起始位置</param>
		public FileSecCache(string FullFileName, System.IO.Stream Stream, long PosBegin = 0)
			: this()
		{
			this.FullFileName = FullFileName;
			this.FS = Stream;

			Cache = new List<byte>();

			this.PosBegin = PosBegin;
		}

		/// <summary>
		/// 保证缓存区不超过2MB
		/// </summary>
		public void Keep2MB()
		{
			int n = Cache.Count - _MaxSize;
			if (n > 0)
			{
				FlushToStream();//确保不清除到未写入流的数据
				lock (_lockCache)
				{
					Cache.RemoveRange(0, n);
					PosBegin += n;
					Flushed -= n;

					if (Flushed < 0)
					{
						Flushed = 0;
					}
				}
			}
		}

		/// <summary>
		/// 从流读入缓存区(增量方式)
		/// </summary>
		/// <param name="Count">最大读取量(单位:B)</param>
		public void ReadFromStream(int Count)
		{
			FS.Seek(PosBegin + Cache.Count, System.IO.SeekOrigin.Begin);

			byte[] tmp = new byte[Count];
			int readCount = FS.Read(tmp, 0, Count);
			byte[] tmp2 = new byte[readCount];
			tmp.CopyTo(tmp2, 0);
			lock (_lockCache)
			{
				Cache.AddRange(tmp2);
				Flushed += readCount;//从流中读入的内容不用再刷回去
			}

			Keep2MB();
		}

		/// <summary>
		/// 将本块未写入部分写入流(覆盖原内容)
		/// </summary>
		public void FlushToStream()
		{
			int CahceCount = Cache.Count;
			if (CahceCount > Flushed)
			{
				// 从全局获取流的锁(文件锁)
				if (!GlobalObject.Locks.Keys.Contains(FullFileName))
				{
					GlobalObject.Locks.Add(FullFileName, new object());
				}

				lock (GlobalObject.Locks[FullFileName])
				{
					FS.Seek(PosBegin + Flushed, System.IO.SeekOrigin.Begin);

					FS.Write(Cache.ToArray(), Flushed, CahceCount - Flushed);
					Flushed = CahceCount;
				}
			}
		}

		/// <summary>
		/// 从缓存的当前位置开始写入
		/// </summary>
		public void WriteRange(byte[] Data, long idxBegin = 0, long Count = 0)
		{
			if (Count <= 0)
			{
				Count = Data.Length;
			}

			byte[] tmp = new byte[Count];
			for (long i = idxBegin; i < Count; i++)
			{
				tmp[i - idxBegin] = Data[i];
			}

			lock (_lockCache)
			{
				Cache.AddRange(tmp);
			}

			Keep2MB();
		}

		/// <summary>
		/// 将已写入部分移出缓存
		/// </summary>
		public void ClearFlushed()
		{
			lock (_lockCache)
			{
				Cache.RemoveRange(0, Flushed);
				PosBegin += Flushed;
				Flushed = 0;
			}
		}
	}
}
